package com.unitechs.commons.utils;


import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.io.Serializable;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

//TODO 还需优化，还未解决key的长度限制问题
@Slf4j
public final class ZyucAuthenticationUtil {
    private ZyucAuthenticationUtil() {
        //防止实例化
        throw new AssertionError();
    }

    private static final String RSA = "RSA";

    /**
     * 加密
     *
     * @param o             需要加密的对象
     * @param privateKeyStr 私钥
     * @return token
     */
    public static String zyucEncryptToToken(Object o, String privateKeyStr) {
        String encodedData = "";
        try {
            String json = ZyucObjectUtil.zyucConvertObjectToJson(o);
            // 将私钥Base64字符串转换为私钥对象
            PrivateKey privateKey = decodeToPrivateKey(privateKeyStr);
            // 使用私钥加密
            byte[] encryptedBytes = encryptWithPrivateKey(json.getBytes(), privateKey);
            //将加密后的byte转String类型
            String encryptedData = Base64.getEncoder().encodeToString(encryptedBytes);
            encodedData = URLEncoder.encode(encryptedData, "UTF-8");
            log.info("加密后的数据：{}", encodedData);
        } catch (Exception e) {
            log.error("加密报错");
        }
        return encodedData;
    }


    /**
     * 获取密钥对
     */
    public static RsaKeyEntity zyucGetKeyPair() throws Exception {
        //获取密钥对
        KeyPair keyPair = generateKeyPair();
        //获取私钥
        PrivateKey aPrivate = keyPair.getPrivate();
        //获取公钥
        PublicKey aPublic = keyPair.getPublic();
        //私钥转String类型
        String privateKeyStr = encodeToBase64(aPrivate);
        //公钥转String类型
        String publicKeyStr = encodeToBase64(aPublic);
        //赋值给返回对象
        RsaKeyEntity rsaKeyEntity = new RsaKeyEntity();
        rsaKeyEntity.setPrivateKey(privateKeyStr);
        rsaKeyEntity.setPublicKey(publicKeyStr);
        return rsaKeyEntity;
    }


    /**
     * 解密
     *
     * @param publicKeyBase64
     * @param token
     */
    public static String zyucDecryptAndRedirect(String publicKeyBase64, String token) {
        String decryptedData = "";
        try {
            //去除转义
            token = URLDecoder.decode(token, "UTF-8");
            // 将公钥Base64字符串转换为公钥对象
            PublicKey publicKey = decodeToPublicKey(publicKeyBase64);
            // 使用公钥解密
            byte[] decryptedBytes = decryptWithPublicKey(Base64.getDecoder().decode(token), publicKey);
            decryptedData = new String(decryptedBytes);
            log.info("解密后的数据：{}", decryptedData);
        } catch (Exception e) {
            log.error("解密失败",e);
        }
        return decryptedData;
    }


    /**
     * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<对内提供>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
     */

    // 生成RSA密钥对
    private static KeyPair generateKeyPair() throws Exception {
        // 创建RSA密钥对生成器对象
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA);
        // 设置密钥长度为2048位
        keyPairGenerator.initialize(2048);
        // 生成RSA密钥对并返回
        return keyPairGenerator.generateKeyPair();
    }

    // 将Key对象转换成Base64字符串
    private static String encodeToBase64(Key key) {
        // 获取Key对象的字节数组表示
        byte[] encodedKey = key.getEncoded();
        // 将字节数组进行Base64编码并返回编码后的字符串
        return Base64.getEncoder().encodeToString(encodedKey);
    }

    // 将Base64字符串解码为私钥对象
    private static PrivateKey decodeToPrivateKey(String privateKeyBase64) throws Exception {
        // 将Base64字符串解码为字节数组
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
        // 创建私钥的KeySpec对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        // 以PKCS#8 格式编码
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        // 将以 PKCS#8 格式编码的私钥字节数组转换为私钥对象
        return keyFactory.generatePrivate(pkcs8EncodedKeySpec);

    }

    // 将Base64字符串解码为公钥对象
    private static PublicKey decodeToPublicKey(String publicKeyBase64) throws Exception {
        // 将Base64字符串解码为字节数组
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
        // 创建公钥的KeySpec对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        //以 X.509 格式编码
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);
        // 将以  X.509 格式编码的公钥字节数组转换为公钥对象
        return keyFactory.generatePublic(x509EncodedKeySpec);
    }

    // 使用私钥加密
    private static byte[] encryptWithPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
        // 创建Cipher对象，并指定加密算法为RSA
        Cipher cipher = Cipher.getInstance(RSA);
        // 使用私钥初始化Cipher对象，设置为加密模式
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        // 对数据进行加密并返回加密后的字节数组
        return cipher.doFinal(data);
    }

    // 使用公钥解密
    private static byte[] decryptWithPublicKey(byte[] encryptedData, PublicKey publicKey) throws Exception {
        // 创建Cipher对象，并指定解密算法为RSA
        Cipher cipher = Cipher.getInstance(RSA);
        // 使用公钥初始化Cipher对象，设置为解密模式
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        // 对加密数据进行解密并返回解密后的字节数组
        return cipher.doFinal(encryptedData);
    }

    @Data
    public static class RsaKeyEntity implements Serializable {
        private String publicKey;
        private String privateKey;

        public RsaKeyEntity() {
        }

        public RsaKeyEntity(String publicKey, String privateKey) {
            this.publicKey = publicKey;
            this.privateKey = privateKey;
        }
    }
}
